﻿using System;
using System.Threading;
using System.Windows.Controls;

namespace Microsoft.Phone.Controls.Primitives
{
    /// <summary>
    /// Basic <see cref="ILoopingSelectorDataSource"/> implementation for <see cref="DateTime"/> based <see cref="LoopingSelector"/>.
    /// </summary>
    public abstract class DataSource : ILoopingSelectorDataSource
    {
        private ExtendedDateTimeWrapper _selectedItem;
        private EventHandler<SelectionChangedEventArgs> _selectionChanged;

        /// <summary>
        /// When implemented gets previous or next item relatively to <paramref name="relativeDate"/>
        /// </summary>
        /// <param name="relativeDate"></param>
        /// <param name="delta"></param>
        /// <returns></returns>
        protected abstract DateTime? GetRelativeTo(DateTime relativeDate, int delta);

        #region Implementation of ILoopingSelectorDataSource

        /// <summary>
        /// Get the next datum, relative to an existing datum.
        /// </summary>
        /// <param name="relativeTo">The datum the return value will be relative to.</param>
        /// <returns>
        /// The next datum.
        /// </returns>
        public object GetNext(object relativeTo)
        {
            var nullable = GetRelativeTo(((ExtendedDateTimeWrapper)relativeTo).DateTime, 1);

            return !nullable.HasValue ? null : new ExtendedDateTimeWrapper(nullable.Value);
        }

        /// <summary>
        /// Get the previous datum, relative to an existing datum.
        /// </summary>
        /// <param name="relativeTo">The datum the return value will be relative to.</param>
        /// <returns>
        /// The previous datum.
        /// </returns>
        public object GetPrevious(object relativeTo)
        {
            var nullable = GetRelativeTo(((ExtendedDateTimeWrapper)relativeTo).DateTime, -1);

            return !nullable.HasValue ? null : new ExtendedDateTimeWrapper(nullable.Value);
        }

        /// <summary>
        /// The selected item. Should never be null.
        /// </summary>
        public object SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                if (value == _selectedItem) return;

                var wrapper = (ExtendedDateTimeWrapper)value;

                if (((wrapper != null) && (_selectedItem != null)) && (wrapper.DateTime == _selectedItem.DateTime)) return;

                object tmp = _selectedItem;

                _selectedItem = wrapper;

                var selectionChanged = _selectionChanged;

                if (selectionChanged != null)
                {
                    selectionChanged(this, new SelectionChangedEventArgs(new[] { tmp }, new object[] { _selectedItem }));
                }
            }
        }

        /// <summary>
        /// Raised when the selection changes.
        /// </summary>
        public event EventHandler<SelectionChangedEventArgs> SelectionChanged
        {
            add
            {
                EventHandler<SelectionChangedEventArgs> handler2;
                var selectionChanged = _selectionChanged;

                do
                {
                    handler2 = selectionChanged;
                    var handler3 = (EventHandler<SelectionChangedEventArgs>)Delegate.Combine(handler2, value);
                    selectionChanged = Interlocked.CompareExchange(ref _selectionChanged, handler3, handler2);
                } while (selectionChanged != handler2);
            }
            remove
            {
                EventHandler<SelectionChangedEventArgs> handler2;
                var selectionChanged = _selectionChanged;

                do
                {
                    handler2 = selectionChanged;
                    var handler3 = (EventHandler<SelectionChangedEventArgs>)Delegate.Remove(handler2, value);
                    selectionChanged = Interlocked.CompareExchange(ref _selectionChanged, handler3, handler2);
                } while (selectionChanged != handler2);
            }
        }

        #endregion
    }
}
